/**
 * Class AjaxManager. Handles Ajax communications.
 * 
 * @author Maxime Ollagnier
 */
AjaxManager = function() {
};

/** Web app base URI */
AjaxManager.BASE_URI = CONTEXT_ROOT + '/new/';

/** Delay in ms before redirecting to login page when session expired */
AjaxManager.SESSION_EXPIRATION_LOGIN_URI_REDIRECTION_TIME = 2000;

/** Cache of the current XmlHTTP requests */
AjaxManager.currentRequests = {};

/** Current processing downloads */
AjaxManager.currentDownloads = {};

/** Current processing form submits */
AjaxManager.currentSubmits = {};

/**
 * Returns the host base URL
 */
AjaxManager.getBaseUrl = function() {
	var baseUrl = document.location.href;
	if (baseUrl.indexOf(AjaxManager.BASE_URI) < 0) {
		console.error('Base URI is incorrect : [' + AjaxManager.BASE_URI + ']');
	}
	return baseUrl.substring(0, baseUrl.indexOf(AjaxManager.BASE_URI)) + AjaxManager.BASE_URI;
};

/**
 * Handles the JSON request. Checks to see if the current request is already
 * being processed and handles errors. if 'silent' parameter is true, no error
 * message is popped to the user (a log is still displayed on console)
 */
AjaxManager.getJSON = function(uri, parameters, callback, silent) {
	parameters = Util.getObject(parameters);
	AjaxManager.progress(true);
	// Makes the actual AJAX request
	$.ajax({
		type : 'POST',
		dataType : 'json',
		url : AjaxManager.BASE_URI + uri,
		data : parameters
	})
	// Handles communication success
	.success(function(result) {
		AjaxManager.progress(false);
		result = AjaxManager.handleResult(uri, result, silent);
		Util.getFunction(callback)(result);
	})
	// Handles communication error
	.error(function(xhr, ajaxOptions, thrownError) {
		AjaxManager.progress(false);
		// TODO see below AjaxManager.alertErrorServer();
		Util.getFunction(callback)({
			'success' : false
		});
		if (xhr.responseText.indexOf('http.499') >= 0) {
			AjaxManager.sessionExpired();
		} else if (xhr.state() == 'rejected' && xhr.responseText == '') {
			AjaxManager.communicationError();
		}
	});
};

/**
 * Handles the result coming from the server. Displays warning and error
 * messages, sets result.success to true or false and returns it. if 'silent'
 * parameter is true, no error message is popped to the user (a log is still
 * displayed on console)
 */
AjaxManager.handleResult = function(uri, result, silent) {
	result.success = true;
	if (Util.checkArray(result.warnMessages) && result.warnMessages.length > 0) {
		for ( var i = 0; i < result.warnMessages.length; i++) {
			console.warn(result.warnMessages[i]);
		}
	}
	// If there are errors
	if (Util.checkArray(result.errors) && result.errors.length > 0) {
		
		// Opens console log group
		if (Util.checkFunction(console.group)) {
			console.group('SERVER ERROR [uri=' + uri + ']');
		}
		
		// Treats every error
		var publicMessages = [];
		var publicDetails = [];
		for (var i = 0; i < result.errors.length; i++) {
			
			// If the error has an i18n reason adds it to the public message
			if (Util.checkStringNotEmpty(result.errors[i].reason) && I18n.has(result.errors[i].reason)) {
				publicMessages.push(I18n.get(result.errors[i].reason, true, result.errors[i].parameters));
			}
			// Otherwise adds some technical details
			else {
				if (Util.checkStringNotEmpty(result.errors[i].message)) {
					publicDetails.push('Message : ' + result.errors[i].message);
				}
				if (Util.checkNumber(result.errors[i].id)) {
					publicDetails.push('Logs : #' + result.errors[i].id);
				}
			}
			
			// Logs the reason, message and ID
			var errorLogged = false;
			if (Util.checkStringNotEmpty(result.errors[i].reason)) {
				console.error('Reason : ' + I18n.get(result.errors[i].reason, true, result.errors[i].parameters));
				errorLogged = true;
			}
			if (Util.checkStringNotEmpty(result.errors[i].message)) {
				console[errorLogged ? 'log' : 'error']('Message : ' + result.errors[i].message);
				errorLogged = true;
			}
			if (Util.checkNumber(result.errors[i].id)) {
				console[errorLogged ? 'log' : 'error']('Logs : #' + result.errors[i].id);
			}
		}
		
		// Closes console log group
		if (Util.checkFunction(console.group)) {
			console.groupEnd();
		}
		
		// If the flag silent is not true, popup the error
		if (silent != true) {
			AjaxManager.alertErrorServer(publicMessages, publicDetails);
		}
		result.success = false;
	}
	return result;
};

/**
 * Popup an error alert with the given messages, or display a default message if
 * no message is given. If 'details' array exist and is not empty, it is used
 * to display more information about the error
 */
AjaxManager.alertErrorServer = function(messages, details) {
	var jQPopupContent = $('<div></div>');
	
	// Public messages addition to the popup content
	if (Util.checkArrayNotEmpty(messages)) {
		for (var i = 0; i < messages.length; i++) {
			jQPopupContent.append(messages[i] + '<br/>');
		}
	} else {
		jQPopupContent.append(I18n.get('server.error.popup.content') + '<br/>');
	}
	
	// Public technical details addition to a collapsible module in the popup content
	if (Util.checkArrayNotEmpty(details)) {
		var jQDetails = $('<div class="popup_err_details"></div>');
		var jQCollapsibleTitle = $('<span class="popup_err_details_title">' + I18n.get('server.error.popup.details') + '</span>');
		var jQCollapsibleContent = $('<div class="popup_err_details_content"></div>');
		for (var i = 0; i < details.length; i++) {
			jQCollapsibleContent.append(details[i] + '<br/>');
		}
		var detailsCollapsible = new CollapsibleModule({
			'title' : jQCollapsibleTitle,
			'content' : jQCollapsibleContent,
			'showCollapsibleIcon' : 'none'
		});
		jQDetails.append(detailsCollapsible.buildJQ());
		jQPopupContent.append(jQDetails);
	}
	
	// Displays the popup
	PopupModule.getInstance().clear();
	PopupModule.getInstance().setTitle(I18n.get('server.error.popup.title'));
	PopupModule.getInstance().setContent(jQPopupContent);
	PopupModule.getInstance().addButton('OK');
	PopupModule.getInstance().show();
};

/**
 * Handles the JSON request. Checks to see if the current request is already
 * being processed and handles errors.
 */
AjaxManager.download = function(uri, parameters, callback) {
	var downloadId = moment().valueOf();
	var iFrameJQ = AjaxManager.buildIFrameJQ(downloadId);
	iFrameJQ.bind('load', function() {
		Util.getFunction(callback)();
	});
	var url = AjaxManager.getBaseUrl() + uri;
	if (Util.checkObject(parameters)) {
		var first = true;
		$.each(parameters, function(key, val) {
			if (first) {
				url += '?';
			} else {
				url += '&';
			}
			first = false;
			url += key + '=' + escape(encodeURIComponent(val));
		});
	}
	iFrameJQ.attr('src', url);
};

/**
 * Submit the given JQ form to the given URI and calls the specified callback.
 * 
 * Here this is a ninja trick to allow form submissions to execute
 * asynchronously a callback method with the server json result. The form's
 * target is set to the name of a brand new iFrame. This iFrame load event is
 * listened to. When the iFrame is loaded with the server response, the
 * specified callback is called.
 */
AjaxManager.submitForm = function(uri, formJQ, callback) {
	var submitId = moment().valueOf();
	formJQ.attr('action', AjaxManager.BASE_URI + uri);
	formJQ.attr('target', submitId);
	var iFrameJQ = AjaxManager.buildIFrameJQ(submitId);
	iFrameJQ.bind('load',
			function() {
				var jsonResult = $($(this).prop('contentDocument'))
						.find('body').text();
				if (!Util.check($(this).prop('contentDocument'))) {
					jsonResult = $($(this).prop('contentWindow').document)
							.find('body').text(); // For stupid IE
				}
				var result = {};
				if (Util.checkStringNotEmpty(jsonResult)) {
					result = eval('(' + jsonResult + ')');
				}
				$(this).unbind('load');
				$(this).remove();
				AjaxManager.handleResult(uri, result);
				Util.getFunction(callback)(result);
			});
	try {
		formJQ.submit();
	} catch (e) {
		iFrameJQ.unbind('load');
		iFrameJQ.remove();
		console.error(e);
		// TODO see below AjaxManager.alertErrorServer();
		Util.getFunction(callback)({
			'success' : false
		});
	}
};

/**
 * Builds and returns a JQ invisible iFrame
 */
AjaxManager.buildIFrameJQ = function(id) {
	var iFrameJQ = $('<iframe id="' + id + '" name="' + id + '"></iframe>');
	iFrameJQ.css('position', 'absolute');
	iFrameJQ.css('top', '-1000px');
	$('body').append(iFrameJQ);
	return iFrameJQ;
};

AjaxManager.sessionExpired = function() {
	PopupModule.getInstance().clear();
	PopupModule.getInstance().show({
		'title' : I18n.get('session.expired'),
		'content' : I18n.get('session.expired.desc')
	});

	setTimeout(function() {
		window.location = CONTEXT_ROOT + NavigationManager.getTimeoutURI();
	}, AjaxManager.SESSION_EXPIRATION_LOGIN_URI_REDIRECTION_TIME);
};

AjaxManager.communicationError = function(){
	PopupModule.getInstance().clear();
	PopupModule.getInstance().addButton('OK');
	PopupModule.getInstance().show({
		'title' : I18n.get('communication.error.title'),
		'content' : I18n.get('communication.error.content')
	});
};

/**
 * Show/hide a progress gif next to the cursor
 */
AjaxManager.progress = function(display) {
	if (!Util.check(AjaxManager.progressDiv)){
		AjaxManager.progressDiv = $('<div id="progress-cursor" class="loading" style="z-index:1000;position:fixed;top:-50px;left:-50px;"></div>');
		$('body').append(AjaxManager.progressDiv);
	}
	if (Util.checkBool(display) && display) {
		var pos = {'left': (MouseManager.getX() + 10) + 'px', 'top': (MouseManager.getY() - 20) + 'px'};
		AjaxManager.progressDiv.offset(pos);
		AjaxManager.progressDiv.show();
		$(document).bind('mousemove.progress',function() {
			pos = {'left': MouseManager.getX() + 10, 'top': MouseManager.getY() - 20};
			AjaxManager.progressDiv.offset(pos);
		});
	} else {
		$(document).unbind('mousemove.progress');
		AjaxManager.progressDiv.offset({'left' : '-50px', 'top' : '-50px'});
		AjaxManager.progressDiv.hide();
	}
};
